# @author: Samuel Taylor <samtaylor.uk@gmail.com>
# @package: winecellar
# @license: GNU GPLv3
#
# Copyright (C) 2010  Samuel Taylor <samtaylor.uk@gmail.com>
#----------------------------------------------------------------------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#-----------------------------------------------------------------------

__author__ = 'Samuel Taylor <samtaylor.uk@gmail.com>'

import os
import shutil
import tempfile
import re
from lxml import etree
import WineCellar.Config
from WineCellar.Preferences import Preferences
from WineCellar.Logger import logger

#console = loggerging.StreamHandler()
#console.setLevel(loggerging.DEBUG)
# set a format which is simpler for console use
#formatter = loggerging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
# tell the handler to use this format
#console.setFormatter(formatter)
# add the handler to the root l

class Bottle:
    def __init__(self, bottleName):
        self.preferences = Preferences()
        
        self.bottleName = bottleName
        self.bottlePath = Config.__bottlepath__ + bottleName
        
        parser = etree.XMLParser(remove_blank_text=True)
        self.tree = etree.parse(str(self.bottlePath +'/bottle.xml'), parser)

        #for element in tree.iter():
          #  self.bottleConfigelement.tag, element.text))
        
    
    def run(self, command, arg = None):
        if command == "winver":
            command = "C:\\windows\\system32\\winver.exe"
        elif re.search("\.msi", command):
            command = "msiexec /i " + command
        
        if arg == None:
            run = "WINEDEBUG=-all WINEPREFIX=\"" + self.bottlePath + "\" " + "wine " + "\"" + command + "\""
        else:
            run = "WINEDEBUG=-all WINEPREFIX=\"" + self.bottlePath + "\" " + "wine " + "\"" + command + "\" " + arg
        #print run
        code = os.system( run )
        #print code
        logger.debug("Wine \""+run+"\" return code: " + str(code))
        return code
    
    def getwinecoloursfromgtk(self):
        """
        Returns a dict containing colours from 
        the current gtk theme and their wine names
        """
        # Get some colors from GTK
        # gtk.Style object:
        # http://www.moeraki.com/pygtkreference/pygtk2reference/class-gtkstyle.html

        # Create a window to scrape colors from
        
        import gtk
        import gconf
        
        window = gtk.Window()

        button = gtk.Button()
        vbox = gtk.VBox()
        vbox.add(button)

        scrollbar =  gtk.VScrollbar()
        vbox.add(scrollbar)

        menubar = gtk.MenuBar()
        menuitem = gtk.MenuItem()
        menubar.add(menuitem)
        vbox.add(menubar)

        window.add(vbox)

        # On show_all(), these are all converted from gtk.Style objects with wrong
        # colors into __main__.EngineStyle objects with correct colors.
        window.show_all()
        window.hide()

        # Tooltips
        # http://www.daa.com.au/pipermail/pygtk/2005-November/011353.html
        tooltip = gtk.Window()
        tooltip.set_name('gtk-tooltips')
        tooltip.show_all()
        tooltip.hide()

        # All of Wine's 31 possible user.reg color values
        # Asterisk * next to comment if I've found a value that works for most themes
        # Question mark ? next to comment if not sure or can't find anywhere to scrape from

        # Some descriptions:
        # http://www.quimp.net/gamemaker/system-colors
        # http://www.endolith.com/wordpress/2008/08/03/wine-colors/
        # http://support.microsoft.com/kb/58336
        # http://msdn.microsoft.com/en-us/library/system.drawing.systemcolors_properties(VS.80).aspx


        return {

        # Doesn't seem to be anything to scrape for these.

        #? Active title bar text - white since we're guessing on dark selection color for titlebar
        'TitleText':             self.format_color_string( window.style.white ),

        #? Left end of active title bar - guess on selection color like Clearlooks
        'ActiveTitle':           self.format_color_string( window.style.dark[gtk.STATE_SELECTED] ), 

        #? Right end of active title bar - lighter version for gradient
        'GradientActiveTitle':   self.format_color_string( window.style.light[gtk.STATE_SELECTED] ), 

        #? Active window border - same as ButtonFace like Clearlooks
        'ActiveBorder':          self.format_color_string( button.style.bg[gtk.STATE_INSENSITIVE] ),

        #? Inactive title bar text - white since we're guessing on dark color for titlebar
        'InactiveTitleText':     self.format_color_string( window.style.white ), 

        #? Left end of inactive title bar - darker color for gradient
        'InactiveTitle':         self.format_color_string( window.style.dark[gtk.STATE_NORMAL] ), 

        #? Right end of inactive title bar - guess on base color
        'GradientInactiveTitle': self.format_color_string( window.style.base[gtk.STATE_NORMAL] ),  
      
        #? Inactive window border - same as ButtonFace
        'InactiveBorder':        self.format_color_string( button.style.bg[gtk.STATE_INSENSITIVE] ), 

        #? BG color of MDI, which aren't used in GTK: use same color as Window, like Glade
        'AppWorkSpace':          self.format_color_string( window.style.base[gtk.STATE_NORMAL] ),

        # Get desktop background color from gconf
        'Background':            gconf.Client().get_value("/desktop/gnome/background/primary_color"), 

        #* Button/tab text and glyphs
        'ButtonText':            self.format_color_string( button.style.fg[gtk.STATE_NORMAL] ), 

        #* Outermost button higlight / Grayed-out button text shadow
        'ButtonHilight':         self.format_color_string( button.style.light[gtk.STATE_INSENSITIVE] ), 

        #* Inner button highlight, usually same as ButtonFace
        'ButtonLight':           self.format_color_string( button.style.bg[gtk.STATE_INSENSITIVE] ), 

        #* Background for buttons and all 3D objects
        'ButtonFace':            self.format_color_string( button.style.bg[gtk.STATE_INSENSITIVE] ), 

        #* No idea what this does - Set to same as ButtonFace for now
        'ButtonAlternateFace':   self.format_color_string( button.style.bg[gtk.STATE_INSENSITIVE] ), 

        #* Shadows of buttons / Grayed-out button text
        'ButtonShadow':          self.format_color_string( button.style.dark[gtk.STATE_INSENSITIVE] ), 

        #* Outermost shadow of buttons
        'ButtonDkShadow':        self.format_color_string( button.style.black ), 

        # Grayed out text in windows, like labels for unavailable widgets
        'GrayText':              self.format_color_string( window.style.fg[gtk.STATE_INSENSITIVE] ),

        # Background of selected text
        'Hilight':               self.format_color_string( window.style.base[gtk.STATE_SELECTED] ), 

        # Selected text
        'HilightText':           self.format_color_string( window.style.fg[gtk.STATE_SELECTED] ), 

        # Single-click navigation hover color, doesn't seem to exist in GTK; use lighter ButtonFace color, like CompizConfig Settings Manager
        'HotTrackingColor':      self.format_color_string( window.style.light[gtk.STATE_NORMAL] ), 

        #* ToolTip text
        'InfoText':              self.format_color_string( tooltip.style.fg[gtk.STATE_NORMAL] ), 

        #* ToolTip background
        'InfoWindow':            self.format_color_string( tooltip.style.bg[gtk.STATE_NORMAL] ), 

        # Background for menus, also background for menu bars in 3D mode
        'Menu':                  self.format_color_string( menuitem.style.light[gtk.STATE_ACTIVE] ), 

        # Background for menu bars - rarely seen due to 3D menus
        'MenuBar':               self.format_color_string( menubar.style.bg[gtk.STATE_NORMAL] ), 

        # Highlight for flat menus - in 3D mode, Hilight is used instead
        'MenuHilight':           self.format_color_string( menuitem.style.bg[gtk.STATE_PRELIGHT] ), 

        # Menu text
        'MenuText':              self.format_color_string( menuitem.style.fg[gtk.STATE_NORMAL] ), 

        # Background color of scrollbar, but only in some apps.
        'Scrollbar':             self.format_color_string( scrollbar.style.bg[gtk.STATE_ACTIVE] ), 

        # Background color of notepad, for instance
        'Window':                self.format_color_string( window.style.base[gtk.STATE_NORMAL] ), 

        # Glow around focused widget
        'WindowFrame':           self.format_color_string( button.style.mid[gtk.STATE_SELECTED] ), 

        # Text in notepad, for instance
        'WindowText':            self.format_color_string( window.style.text[gtk.STATE_NORMAL] ), 

        }

    # For wine colours from gtk -- thanks endolith
    def format_color_string(self, Color):
        """ Convert 48-bit gdk.Color to 24-bit "RRR GGG BBB" triple. """
        return "%s %s %s" % (Color.red/256, Color.green/256,  Color.blue/256)

    def format_hex_color(self, color):
        """ Convert from #rrrrggggbbbb string to decimal "RRR GGG BBB" triple. """
        r, g, b = color[1:3], color[5:7], color[9:11]
        return "%s %s %s" % (int(r,16), int(g,16), int(b,16))
    
    def SetColoursFromGtk(self):
        for name, data in self.getwinecoloursfromgtk().iteritems():
            self.setRegistry("HKCU\\Control Panel\\Colors\\%s" % name, data)
            
    def setRegOwner(self):
        name = self.preferences.getKey("owner")
        self.setRegistry("HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\RegisteredOwner", name )
        self.setRegistry("HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\RegisteredOwner", name )

    def setRegCompany(self):
        name = self.preferences.getKey("company")
        self.setRegistry("HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\RegisteredOrganization", name )
        self.setRegistry("HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\RegisteredOrganization", name )
        
    def getRegistry( self, path, key=None ):
        """
        Expects a path in the form of HKLM\\\\..\\\\.. where HKLM = System.reg
        and HKCU = User.reg, all in all this is a pretty nasty hack :/
        TODO: Input should be sanitised someone, expect the unexpected!
        """
        if path.find("HKCU") != -1:
            if os.path.isfile( self.bottlePath+"user.reg" ):
                reg_file = self.bottlePath+"user.reg"
                path = path.replace("HKCU\\\\", "")
            else:
                logger.error("Cannot locate user.reg %s " % (self.bottlePath+"user.reg"))
                return False
        
        if path.find("HKLM") != -1:
            if os.path.isfile( os.path.join(self.bottlePath, "system.reg") ):
                reg_file = self.bottlePath+"system.reg"
                path = path.replace("HKLM\\\\", "")
            else:
                logger.error("Cannot locate system.reg %s " % (self.bottlePath+"system.reg"))
                return False
        if not key:
            reg = re.compile("(.*)\\\\(.*)\Z")
            m = reg.match(path)
            path = m.group(1)
            key = m.group(2)
        data = ""
        inkey = False

        for line in file(reg_file):
            if line.find(path) != -1:
                inkey = True
            elif inkey:
                if line.find("[") != -1:
                    inkey = False
                    continue
                if line.find("=") == -1:
                    continue
                line = line.replace( "\"", "").replace( "\n", "")
                [thiskey, thisval] = line.split( "=", 1 )
                if thiskey == key:
                    data = thisval
                    break
        return data.replace("dword:", "", 1)

    def setRegistry(self, path, value):
        """
        This is a hack for setting individual registry keys
        it should make it relatively easy to implement lots of 
        registry hacks in the UI...
        We clean up the input and will accept \\\\ or \\ and
        we strip any square brackets too, then we generate
        a regedit 4 file then pass it over to the execute method
        """
        path.replace("\\\\", "\\")
        path.replace("[", "")
        path.replace("]", "")
        if path.find("HKLM") != -1:
            path = path.replace("HKLM", "HKEY_LOCAL_MACHINE")
        if path.find("HKCU") != -1:
            path = path.replace("HKCU", "HKEY_CURRENT_USER")

        reg = re.compile("(.*)\\\\(.*?)\Z")
        m = reg.match(path)
        path = m.group(1)
        key = m.group(2)

        __, tmp_name = tempfile.mkstemp(suffix='.reg') 
        f = open(tmp_name , "wt" )
        f.write("REGEDIT 4\n\n")
        f.write("["+path+"]\n")
        f.write("\""+key+"\"=\""+value+"\"\n")
        f.close()

        self.run("regedit", tmp_name);
        os.remove(tmp_name)

    def unsetRegistry(self, path, key=None):
        """
        This is a hack for unsetting individual registry keys
        it should make it relatively easy to implement lots of 
        registry hacks in the UI...
        We clean up the input and will accept \\\\ or \\ and
        we strip any square brackets too, then we generate
        a regedit 4 file then pass it over to the execute method

        Completely untested function
        """
        path.replace("\\\\", "\\")
        path.replace("[", "")
        path.replace("]", "")
        if path.find("HKLM") != -1:
            path = path.replace("HKLM", "HKEY_LOCAL_MACHINE")
        if path.find("HKCU") != -1:
            path = path.replace("HKCU", "HKEY_CURRENT_USER")

        __, tmp_name = tempfile.mkstemp(suffix='.reg') 
        f = open(tmp_name , "wt" )
        f.write("REGEDIT 4\n\n")
        if key:
            f.write("["+path+"]\n")
            f.write("\""+key+"\"=-\n")
        else:
            f.write("[-"+path+"]\n")
        f.close()

        self.run("regedit", tmp_name);
        os.remove(tmp_name)
    
    def setup(self):
        self.setRegOwner()
        self.setRegCompany() 
        
    def addInstalled(self, shortName, version):
        """
        Adds an installed application to the bottles config xml.
        """
        root = self.tree.getroot()
        app = etree.SubElement(root, "installed", version=version).text = shortName

        f = open(str(self.bottlePath +'/bottle.xml'), 'w')
        f.write(etree.tostring(self.tree, pretty_print=True, encoding='iso-8859-1'))
        f.close()
        
        #Reload bottle configurion
        parser = etree.XMLParser(remove_blank_text=True)
        self.tree = etree.parse(str(self.bottlePath +'/bottle.xml'), parser)
        
    def listInstalled(self):
        """
        Lists installed applications for the bottle.
        """
        installedList = {}
        
        for element in self.tree.iter("installed"):
            installedList[element.text] = element.attrib['version']
        
        return installedList
    
    def isInstalled(self, shortName):
        try:
            if not self.listInstalled()[shortName] == None:
                return self.listInstalled()[shortName]
                
            else:
                return False
        except:
            return False
